﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

namespace Heimdallr.Async
{ 
    public class CollectionConsumer<T>
    {
        #region Field

        private readonly IProducerConsumerCollection<T> collection;
        private readonly Action<T> action;

        private volatile bool busy;

        #endregion

        #region Property

        public IProducerConsumerCollection<T> Collection
        {
            get { return this.collection; }
        }

        public bool IsBusy
        {
            get { return this.busy; }
        }

        #endregion

        #region Event

        public event EventHandler<CompletedEventArgs> Completed;

        #endregion

        #region Constructor

        public CollectionConsumer(Action<T> action, IProducerConsumerCollection<T> collection)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (collection == null)
                throw new ArgumentNullException("collection");

            this.action = action;
            this.collection = collection;
            this.busy = false;
        }

        #endregion

        #region Method

        public bool AddAndStart(T item)
        {
            if (!this.collection.TryAdd(item))
                return false;

            if (!this.busy)
                this.busy = ThreadPool.QueueUserWorkItem(this.ThreadCallback);

            return true;
        }

        #endregion

        #region Callback

        private void ThreadCallback(object state)
        {
            T item;

            while (this.collection.TryTake(out item))
                this.action(item);

            this.busy = false;
            this.OnCompleted(new CompletedEventArgs());
        }

        #endregion

        #region Raise Event

        protected virtual void OnCompleted(CompletedEventArgs e)
        {
            if (this.Completed != null)
                this.Completed(this, e);
        }

        #endregion
    }

    public class CollectionConsumer<TItem, TState>
    {
        #region Field

        private readonly IProducerConsumerCollection<TItem> collection;
        private readonly Action<TItem, TState> action;

        private volatile bool busy;

        #endregion

        #region Property

        public IProducerConsumerCollection<TItem> Collection
        {
            get { return this.collection; }
        }

        public bool IsBusy
        {
            get { return this.busy; }
        }

        #endregion

        #region Event

        public event CompletedEventHandler<TState> Completed;

        #endregion

        #region Constructor

        public CollectionConsumer(Action<TItem, TState> action, IProducerConsumerCollection<TItem> collection)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (collection == null)
                throw new ArgumentNullException("collection");

            this.action = action;
            this.collection = collection;
            this.busy = false;
        }

        #endregion

        #region Method

        public bool AddAndStart(TItem item)
        {
            return this.AddAndStart(item, default(TState));
        }

        public bool AddAndStart(TItem item, TState state)
        {
            if (!this.collection.TryAdd(item))
                return false;

            if (!this.busy)
                this.busy = ThreadPool.QueueUserWorkItem(this.ThreadCallback, state);

            return true;
        }

        #endregion

        #region Callback

        private void ThreadCallback(object state)
        {
            TItem item;
            TState s = (TState)state;

            while (this.collection.TryTake(out item))
                this.action(item, s);

            this.busy = false;
            this.OnCompleted(CompletedEventArgs.Create(s));
        }

        #endregion

        #region Raise Event

        protected virtual void OnCompleted(CompletedEventArgs<TState> e)
        {
            if (this.Completed != null)
                this.Completed(this, e);
        }

        #endregion
    }
}